package com.opdar.athena.base;

import com.opdar.athena.entities.TokenEntity;
import com.opdar.athena.entities.UserEntity;
import com.opdar.athena.utils.JSONUtil;
import com.opdar.platform.core.session.ISessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.types.Expiration;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Created by shiju on 2017/7/2.
 */
public class RedisSessionManager implements ISessionManager<com.opdar.athena.entities.UserEntity> {
    @Autowired
    private JedisConnectionFactory jedisConnectionFactory;
    private long expirationTime = Constants.SESSION_TIMEOUT;

    @Override
    public void set(String token, UserEntity userEntity, long l) {
        byte[] bytes = JSONUtil.toJSONAsBytes(userEntity);
        RedisConnection connection = jedisConnectionFactory.getConnection();
        try{
            String userId = userEntity.getId();
            byte[] userTokenKey = ("UT_" + userId).getBytes();
            if(connection.exists(userTokenKey)){
                List<TokenEntity> userTokenList = JSONUtil.parseList(connection.get(userTokenKey),TokenEntity.class);
                TokenEntity tokenEntity = new TokenEntity(token);

                if(!userTokenList.contains(tokenEntity)){
                    //不存在token，则判断是否存在该平台用户是否已登陆
                    TokenEntity _tokenEntity = new TokenEntity();
                    _tokenEntity.setPlatform(userEntity.getPlatform());
                    if(userTokenList.contains(_tokenEntity)){
                        //存在则删除已存在token
                        int removeIndex = userTokenList.indexOf(_tokenEntity);
                        _tokenEntity = userTokenList.remove(removeIndex);
                        if(_tokenEntity != null){
                            removeToken(token, _tokenEntity.getUserId() , connection);
                        }
                        connection.del(("TOKEN_"+_tokenEntity.getToken()).getBytes());
                    }
                    //状态不为空则更新用户地址
                    if(userEntity.getStat() != null) {
                        tokenEntity.setHost(userEntity.getStat().getHost());
                        tokenEntity.setPort(userEntity.getStat().getPort());
                    }
                    tokenEntity.setUserId(userEntity.getId());
                    tokenEntity.setPlatform(userEntity.getPlatform());
                    userTokenList.add(tokenEntity);
                    connection.set(userTokenKey,JSONUtil.toJSONAsBytes(userTokenList));
                }else if(userEntity.getStat() != null) {
                    //已存在该token则认为二次绑定更新记录
                    userTokenList.get(userTokenList.indexOf(tokenEntity)).setHost(userEntity.getStat().getHost());
                    userTokenList.get(userTokenList.indexOf(tokenEntity)).setPort(userEntity.getStat().getPort());
                    connection.set(userTokenKey,JSONUtil.toJSONAsBytes(userTokenList));
                }
            }else{
                List<TokenEntity> userTokenList = new ArrayList<TokenEntity>();
                TokenEntity tokenEntity = new TokenEntity(token);
                if(userEntity.getStat() != null && userEntity.getStat().getHost() != null&& userEntity.getStat().getPort() != null){
                    tokenEntity.setHost(userEntity.getStat().getHost());
                    tokenEntity.setPort(userEntity.getStat().getPort());
                }
                tokenEntity.setUserId(userEntity.getId());
                tokenEntity.setPlatform(userEntity.getPlatform());
                userTokenList.add(tokenEntity);
                connection.set(userTokenKey,JSONUtil.toJSONAsBytes(userTokenList));
            }
            connection.set(("TOKEN_"+token).getBytes(),bytes,Expiration.from(l, TimeUnit.MILLISECONDS), RedisStringCommands.SetOption.UPSERT);
        }finally {
            connection.close();
        }
    }

    @Override
    public UserEntity get(String token) {
        RedisConnection connection = jedisConnectionFactory.getConnection();
        try{
            byte[] bytes = connection.get(("TOKEN_"+token).getBytes());
            if(bytes != null){
                return JSONUtil.parseObject(bytes,UserEntity.class);
            }
            return null;
        }finally {
            connection.close();
        }
    }


    @Override
    public void remove(String token) {
        RedisConnection connection = jedisConnectionFactory.getConnection();
        try{
            UserEntity user = get(token);
            if(user != null){
                removeToken(token, user.getId() , connection);
            }
            connection.del(("TOKEN_"+token).getBytes());
        }finally {
            connection.close();
        }
    }

    private void removeToken(String token, String userId ,RedisConnection connection) {
        byte[] userTokenKey = ("UT_" + userId).getBytes();
        if(connection.exists(userTokenKey)){
            List<TokenEntity> userTokenList = JSONUtil.parseList(connection.get(userTokenKey),TokenEntity.class);
            TokenEntity tokenEntity = new TokenEntity(token);
            if(userTokenList.contains(tokenEntity)){
                userTokenList.remove(tokenEntity);
                if(userTokenList.size() == 0){
                    connection.del(userTokenKey);
                }else{
                    connection.set(userTokenKey,JSONUtil.toJSONAsBytes(userTokenList));
                }
            }
        }
    }

    public void removeToken(String token,String userId){
        RedisConnection connection = jedisConnectionFactory.getConnection();
        try{
            removeToken(token,userId,connection);
        }finally {
            connection.close();
        }
    }

    @Override
    public void clearTimeout(String token) {
        RedisConnection connection = jedisConnectionFactory.getConnection();
        try{
            connection.pExpire(("TOKEN_"+token).getBytes(),expirationTime);
        }finally {
            connection.close();
        }
    }

    @Override
    public void destory() {
        jedisConnectionFactory.destroy();
    }

    public boolean exist(String token) {
        byte[] tokenKey = ("TOKEN_" + token).getBytes();
        RedisConnection connection = jedisConnectionFactory.getConnection();
        try{
            return connection.exists(tokenKey);
        }finally {
            connection.close();
        }
    }

    public boolean migrate(String migrateId,String token) {
        RedisConnection connection = jedisConnectionFactory.getConnection();
        try{
            return connection.sAdd(("MIGRATE_"+migrateId).getBytes(),token.getBytes())>0;
        }finally {
            connection.close();
        }
    }

    public boolean useMigrate(String migrateId,String token) {
        RedisConnection connection = jedisConnectionFactory.getConnection();
        try{
            byte[] keyBytes = ("MIGRATE_" + migrateId).getBytes();
            if(connection.sCard(keyBytes)>1) {
                return connection.sRem(keyBytes,token.getBytes()) > 0;
            }else{
                return connection.del(keyBytes) > 0;
            }
        }finally {
            connection.close();
        }
    }

    public List<TokenEntity> getTokenWithUserId(String userId) {
        RedisConnection connection = jedisConnectionFactory.getConnection();
        try{
            byte[] userTokenKey = ("UT_" + userId).getBytes();
            if(connection.exists(userTokenKey)){
                return JSONUtil.parseList(connection.get(userTokenKey),TokenEntity.class);
            }
            return new ArrayList<TokenEntity>();
        }finally {
            connection.close();
        }
    }


    public long getClientCount(String address){
        RedisConnection connection = jedisConnectionFactory.getConnection();
        try{
            byte[] readBuffer = connection.hGet("clients".getBytes(), address.getBytes());
            if(readBuffer == null || readBuffer.length < 8)return 0;
            return (((long)readBuffer[0] << 56) +
                    ((long)(readBuffer[1] & 255) << 48) +
                    ((long)(readBuffer[2] & 255) << 40) +
                    ((long)(readBuffer[3] & 255) << 32) +
                    ((long)(readBuffer[4] & 255) << 24) +
                    ((readBuffer[5] & 255) << 16) +
                    ((readBuffer[6] & 255) <<  8) +
                    (readBuffer[7] & 255));
        }finally {
            connection.close();
        }
    }
    public void setClientCount(String address,long count){
        RedisConnection connection = jedisConnectionFactory.getConnection();
        try{
            byte[] writeBuffer = new byte[8];
            writeBuffer[0] = (byte) (count >> 56);
            writeBuffer[1] = (byte) (count >> 48);
            writeBuffer[2] = (byte) (count >> 40);
            writeBuffer[3] = (byte) (count >> 32);
            writeBuffer[4] = (byte) (count >> 24);
            writeBuffer[5] = (byte) (count >> 16);
            writeBuffer[6] = (byte) (count >> 8);
            writeBuffer[7] = (byte) (count);
            connection.hSet("clients".getBytes(),address.getBytes(),writeBuffer);
        }finally {
            connection.close();
        }
    }

}
